/*
 * HttpParser.cpp
 */

#include "HttpParser.h"
#include <vector>

std::string HttpParser::extraerValorVariable(const std::string &cadena,
		const std::string &nombre, size_t pos) const {
	size_t posIniVar = cadena.find(nombre + "=\"", pos);
	if(posIniVar != std::string::npos) {
		size_t posIniVal = posIniVar + nombre.length() + 2;
		size_t posFinVal = cadena.find("\"", posIniVal);
		if(posFinVal != std::string::npos)
			return std::string(cadena.substr(posIniVal,
					posFinVal - posIniVal));
	}
	return std::string();
}

std::string HttpParser::getCin() const {
	// Ignoro los espacios al leer
	std::cin >> std::noskipws;
	unsigned int size = cgi.getCONTENT_LENGTH();
	std::vector<char> buffer(size);
	std::cin.read(buffer.data(), size);
	return std::string(buffer.data(), size);
}

bool HttpParser::decodificarUrlencoded(const std::string &cadena,
		std::string &salida) const {
	bool error = false;
	size_t i = 0;
	size_t longCadena = cadena.size();
	salida.reserve(longCadena);
	while(i < longCadena && !error) {
		if(cadena[i] == '+') {
			salida.push_back(' ');
		} else if(cadena[i] == '%' && i + 2 < longCadena) {
			unsigned codigo;
			std::stringstream ss(cadena.substr(i + 1, 2));
			if(ss >> codigo) {
				salida.push_back((char) codigo);
				i += 2;
			} else {
				error = true;
			}
		} else {
			salida.push_back(cadena[i]);
		}
		++i;
	}
	return !error;
}

std::string HttpParser::procesarUrlEncoded(const std::string &entrada) {
	std::string salida;
	// Decodifico el url
	if(this->decodificarUrlencoded(entrada, salida)) {
		size_t posIni = 0;
		size_t posFin = salida.find(FIN_VALOR_VARIABLE_URL);
		size_t tamSalida = salida.length();
		bool ultimoPar = false;
		if(posFin == std::string::npos) {
			// Modifico la posFin si solo hay 1 par
			posFin = tamSalida;
			ultimoPar = true;
		}
		while(posFin != std::string::npos) {
			size_t posSepVar = salida.find(FIN_NOMBRE_VARIABLE_URL, posIni);
			std::string nombreVar = salida.substr(posIni, posSepVar - posIni);
			posIni = posSepVar + 1;
			std::string valorVar = salida.substr(posIni, posFin - posIni);
			posIni = posFin + 1;
			posFin = salida.find(FIN_VALOR_VARIABLE_URL, posIni);
			// Identifico al último par
			if(posFin == std::string::npos && !ultimoPar) {
				posFin = tamSalida;
				ultimoPar = true;
			}
			// Cargo el mapa con las variables
			this->varRecibidas[nombreVar] = valorVar;
		}
		return HTTP_OK;
	}
	// Si no se pudo decodificar, devuelvo error de pedido
	return HTTP_BAD_REQUEST;
}

std::string HttpParser::procesarMultipart(const std::string &boundary) {
	std::string datosRecibidos = this->getCin();
	std::string sepPartes = "--" + boundary;
	size_t posSepPartes = datosRecibidos.find(sepPartes);
	size_t longSepPartes = sepPartes.length();
	bool abortar = false;
	while(!abortar && posSepPartes != std::string::npos) {
		size_t iniEncabezado = posSepPartes + longSepPartes;
		posSepPartes = datosRecibidos.find(sepPartes, iniEncabezado);
		if(posSepPartes != std::string::npos) {
			std::string nombreVar = this->extraerValorVariable(datosRecibidos,
					CAMPO_NOMBRE, iniEncabezado);
			std::string valorVar = this->extraerValorVariable(datosRecibidos,
					CAMPO_ARCHIVO, iniEncabezado);
			size_t finEncabezado = datosRecibidos.find(FIN_ENCABEZADO,
					iniEncabezado);
			if(!nombreVar.empty() && finEncabezado != std::string::npos) {
				// Guardo nombre y valor de la variable
				this->varRecibidas[nombreVar] = valorVar;
				// Guardo el cuerpo del valor de la variable
				finEncabezado += std::string(FIN_ENCABEZADO).length();
				size_t tamCuerpo = posSepPartes - finEncabezado;
				this->varMultipartes[valorVar] =
						datosRecibidos.substr(finEncabezado, tamCuerpo);
			} else
				abortar = true;
		}
	}
	return (abortar) ? HTTP_BAD_REQUEST : HTTP_OK;
}

std::string HttpParser::procesarMetodoGet() {
	std::string queryString = this->cgi.getQUERY_STRING();
	size_t querySize = queryString.size();
	if(querySize == 0) {
		return HTTP_SERVER_ERROR;
	} else if (querySize > MAX_LONG_QUERY_STRING) {
		return HTTP_BAD_REQUEST;
	}
	return this->procesarUrlEncoded(queryString);
}

std::string HttpParser::procesarMetodoPost() {
	long longDatos = this->cgi.getCONTENT_LENGTH();
	// Verifico que el tamaño de los datos sea apropiado o aborto
	if(longDatos < 0) {
		return HTTP_SERVER_ERROR;
	} else if (longDatos > MAX_LONG_CONTENIDO_POST) {
		return HTTP_BAD_REQUEST;
	}
	// Identifico el tipo de codificacion de los datos a recibir
	std::string contentType = this->cgi.getCONTENT_TYPE();
	size_t posSepTipo = contentType.find_first_of(";");
	std::string encType = contentType.substr(0, posSepTipo);
	if(encType == TIPO_URLENCODED) {
		// Proceso la entrada codificada
		return this->procesarUrlEncoded(this->getCin());
	} else if (encType == TIPO_MULTIPART) {
		// Extraigo el boundary separador y proceso el contenido multiparte
		std::string idBoundary = "boundary=";
		size_t posIniBoundary = contentType.find(idBoundary, posSepTipo);
		if(posIniBoundary != std::string::npos) {
			std::string boundary = contentType.substr(posIniBoundary
					+ idBoundary.length());
			return this->procesarMultipart(boundary);
		}
	}
	return HTTP_NOT_IMPLEMENTED;
}

std::string HttpParser::recibirMsjHttp() {
	std::string metodo = this->cgi.getREQUEST_METHOD();
	if(!metodo.empty()) {
		if(metodo == "GET") {
			return this->procesarMetodoGet();
		} else if (metodo == "POST") {
			return procesarMetodoPost();
		} else {
			return HTTP_NOT_IMPLEMENTED;
		}
	}
	return HTTP_SERVER_ERROR;
}

std::string HttpParser::getValorVariable(const std::string &variable) const {
	tVarRecibIt it = this->varRecibidas.find(variable);
	if(it != this->varRecibidas.end())
		return it->second;
	return std::string();
}

std::string HttpParser::getArchivo(const std::string &nombVar) const {
	tVarRecibIt it = this->varMultipartes.find(nombVar);
	if(it != this->varMultipartes.end())
		return it->second;
	return std::string();
}

bool HttpParser::getValidezStatusHttp(const std::string &status) const {
	if(!status.empty())
		return (status[0] == HTTP_SUCCESFUL_PREFIX);
	return false;
}

void HttpParser::emitirEncabezadoRtaHttpCgi(std::ostream &os,
		const std::string &tipo, const std::string &charset,
		const std::string &status, const std::string &location) const {
	os << "Content-Type:" << tipo << ";charset=" << charset << "\r\n";
	if(!status.empty())
		os << "Status:" << status << "\r\n";
	if(!location.empty())
		os << "Location:" << location << "\r\n";
	os << "\r\n";
}
